/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.bff.designtime.generator.core.rtgen.managergenerator.mgrImplclass.methodgenerators.createdatamethod;

import com.inspur.edp.bff.designtime.generator.core.rtgen.managergenerator.mgrImplclass.FSMgrImplGenContext;
import com.inspur.edp.caf.generator.baseInfo.ParameterInfo;
import com.inspur.edp.caf.generator.baseInfo.TypeInfo;
import com.inspur.edp.caf.generator.method.ClassMethodGenerator;
import com.inspur.edp.caf.generator.method.ClassMethodGeneratorContext;
import com.inspur.edp.caf.generator.utils.ExpressionUtils;
import com.inspur.edp.cef.api.attr.EnrichedAssoUdtAttribute;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.element.GspElementDataType;
import com.inspur.edp.cef.designtime.api.element.GspElementObjectType;
import com.inspur.edp.cef.entity.entity.IEntityData;
import com.inspur.edp.cef.entity.entity.dynamicProp.DynamicPropSetImpl;
import com.inspur.edp.cef.gencommon.CefPropertyInfo;
import com.inspur.edp.cef.gencommon.MediateType;
import com.inspur.edp.cef.generator.overall.Utils;
import com.inspur.edp.cef.spi.common.UdtManagerUtil;
import com.inspur.edp.das.commonmodel.IGspCommonObject;
import com.inspur.edp.formserver.viewmodel.GspViewModelElement;
import com.inspur.edp.udt.designtime.api.entity.SimpleDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.UnifiedDataTypeDef;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;

public class CreateDataMethodGenerator extends ClassMethodGenerator {
    private FSMgrImplGenContext mgrImplGenContext;
  private IGspCommonObject commonObject;

  public CreateDataMethodGenerator(FSMgrImplGenContext mgrImplGenContext, IGspCommonObject commonObject) {
        this.mgrImplGenContext = mgrImplGenContext;
    this.commonObject = commonObject;
  }

    @Override
    protected ClassMethodGeneratorContext createClassMethodInfoContext() {
        return new CreateDataMethodGenContext(mgrImplGenContext);
    }

    @Override
    protected Block buildMethodBody() {
        Block block = ast.newBlock();
        addNewDataStatement(block);
        addSetIdPropertyStatement(block);

        addSetNestedPropertiesStatements(block);

        addReturnDataStatement(block);
        return block;
    }

    private void addNewDataStatement(Block block) {
        VariableDeclarationFragment variableDeclarationFragment = ast.newVariableDeclarationFragment();
        variableDeclarationFragment.setName(ast.newSimpleName("data"));

        ClassInstanceCreation classInstanceCreation = ast.newClassInstanceCreation();
        classInstanceCreation.setType(ast.newSimpleType(ast.newName(mgrImplGenContext.getMgrGenContext().getFSGeneratorContext().getFsIntermediateTypeInfo().getFSDataInfo().getMainDataType().getClassName())));
        variableDeclarationFragment.setInitializer(classInstanceCreation);

        VariableDeclarationStatement variableDeclarationStatement = ast.newVariableDeclarationStatement(variableDeclarationFragment);
        variableDeclarationStatement.setType(ast.newSimpleType(ast.newName(mgrImplGenContext.getMgrGenContext().getFSGeneratorContext().getFsIntermediateTypeInfo().getFSDataInfo().getMainDataType().getClassName())));
        block.statements().add(variableDeclarationStatement);
    }

    private void addSetIdPropertyStatement(Block block) {
//        MethodInvocation methodInvocation=ast.newMethodInvocation();
//        methodInvocation.setName(ast.newSimpleName("setID"));
//        methodInvocation.setExpression(ast.newSimpleName("data"));
//        methodInvocation.arguments().add(ast.newSimpleName(paramid));
//        block.statements().add(ast.newExpressionStatement(methodInvocation));
    }

    private void addSetNestedPropertiesStatements(Block block) {
//        String objectCode=this.objectCode;
//        //TODO: 主表也给带进来了, 先兼容一下
//        if ((new String("RootNode")).equals(objectCode))
//        {
//            return;
//        }

        MediateType interfaceType = mgrImplGenContext.getMgrGenContext().getFSGeneratorContext().getDataGeneratorContext().getChildInterfaceTypes().get(commonObject.getCode());
        for (IGspCommonField element : commonObject.getContainElements()) {
          if (element.getIsUdt()) {
            addSetNestedPropertyStatement(commonObject.getCode(), block, element);
          } else if (element.getObjectType() == GspElementObjectType.DynamicProp) {
            addSetDynamicPropSetStatement(ast, block, element, mgrImplGenContext);
          }
        }

//        //entityImplGenCtx.getParentContext().getChildInterfaceTypes().get(objectCode).getBaseInterfate();
//        for (Method method : interfaceType.getMethods()) {
//            PropertyGetAtrribute propertyGetAtrribute = (PropertyGetAtrribute) ReflectionUtils.getMethodAnnotation(method, PropertyGetAtrribute.class);
//            if (propertyGetAtrribute == null)
//                continue;
//            CefPropertyInfo cefPropertyInfo = new CefPropertyInfo(propertyGetAtrribute.propName(), method);
//            EnrichedAssoUdtAttribute enrichedAssoAtrribute = (EnrichedAssoUdtAttribute) ReflectionUtils.getMethodAnnotation(method, EnrichedAssoUdtAttribute.class);
//            NestedConfigAtrribute nestedConfigAtrribute = (NestedConfigAtrribute) ReflectionUtils
//                .getMethodAnnotation(method, NestedConfigAtrribute.class);
//            if(enrichedAssoAtrribute != null){
//                addSetEnrichedAssoPropertyStatement(block, enrichedAssoAtrribute, cefPropertyInfo);
//            }
//            else if (nestedConfigAtrribute != null) {
//                addSetNestedPropertyStatement(block, nestedConfigAtrribute, cefPropertyInfo);
//            }
//            else if (IDynamicPropSet.class.isAssignableFrom(method.getReturnType())) {
//                addSetDynamicPropSetStatement(ast, block, cefPropertyInfo, mgrImplGenContext);
//            }
//        }
    }

    public static void addSetDynamicPropSetStatement(AST ast, Block block,IGspCommonField element, FSMgrImplGenContext entityImplGenCtx) {
        MethodInvocation methodInvocation=ast.newMethodInvocation();
        methodInvocation.setName(ast.newSimpleName("set"+element.getLabelID()));
        methodInvocation.setExpression(ast.newSimpleName("data"));
        ClassInstanceCreation classInstanceCreation = ast.newClassInstanceCreation();
        classInstanceCreation.setType(new TypeInfo(DynamicPropSetImpl.class).getType(ast, entityImplGenCtx.getMgrGenContext().getRootCompileUnitGenCtx()));
        methodInvocation.arguments().add(classInstanceCreation);
        block.statements().add(ast.newExpressionStatement(methodInvocation));
    }

    private void addSetEnrichedAssoPropertyStatement(Block block, EnrichedAssoUdtAttribute nestedConfigAtrribute, CefPropertyInfo cefPropertyInfo) {
        MethodInvocation methodInvocation = ast.newMethodInvocation();
        methodInvocation.setName(ast.newSimpleName(cefPropertyInfo.getSetterMethodName()));
        methodInvocation.setExpression(ast.newSimpleName("data"));
        ClassInstanceCreation classInstanceCreation = ast.newClassInstanceCreation();
        classInstanceCreation.setType(
            ast.newSimpleType(
                ast.newName(
                    mgrImplGenContext.getMgrGenContext().getFSGeneratorContext().getDataGeneratorContext().getEnrichedUdtAssoTypes().get("RootNode").get(cefPropertyInfo.getPropertyName()).getFullName())));
        classInstanceCreation.arguments().add(ast.newNullLiteral());
        methodInvocation.arguments().add(classInstanceCreation);

        block.statements().add(ast.newExpressionStatement(methodInvocation));
    }

    private void addSetNestedPropertyStatement(String objectCode, Block block,
        IGspCommonField element) {
      MethodInvocation methodInvocation = ast.newMethodInvocation();
      methodInvocation.setName(ast.newSimpleName(Utils.getSetterMethodName(element.getLabelID())));
      methodInvocation.setExpression(ast.newSimpleName("data"));

      if (!containRefElementNotFromAssoUdt(element)) {
        addCreateDataTypeStatement(methodInvocation, objectCode, element);
      } else {
        addCreateEnrichedAssoUdtStatement(methodInvocation, objectCode, element);
      }
      block.statements().add(ast.newExpressionStatement(methodInvocation));
    }

  private boolean containRefElementNotFromAssoUdt(IGspCommonField element) {
    UnifiedDataTypeDef udtDef = mgrImplGenContext.getMgrGenContext().getFSGeneratorContext()
        .getCefGenContext().getMetadataContent(element.getUdtPkgName(), element.getUdtID());
    SimpleDataTypeDef sdtDef = (SimpleDataTypeDef) ((udtDef instanceof SimpleDataTypeDef) ? udtDef
        : null);
    return sdtDef != null && sdtDef.getObjectType() == GspElementObjectType.Association && element
        .containRefElementNotFromAssoUdt();
  }

  private void addCreateDataTypeStatement(MethodInvocation methodInvocation, String objectCode,
      IGspCommonField element) {
    MethodInvocation createDataInvocation = ast.newMethodInvocation();
    createDataInvocation.setName(ast.newSimpleName("createData"));
    createDataInvocation.setExpression(ast.newName(UdtManagerUtil.class.getTypeName()));
    UnifiedDataTypeDef udt = mgrImplGenContext.getMgrGenContext().getFSGeneratorContext().getCefGenContext()
        .getMetadataContent(element.getUdtPkgName(), element.getUdtID());
    createDataInvocation.arguments().add(ExpressionUtils.getStringLiteral(ast, udt.getUdtType()));

    if (udt instanceof SimpleDataTypeDef) {
      Expression defaultValueExpr = getSimpleUdtDefaultValue(ast, (SimpleDataTypeDef) udt);
      if (defaultValueExpr != null) {
        createDataInvocation.arguments().add(defaultValueExpr);
      }
    }

    methodInvocation.arguments().add(createDataInvocation);
  }

  static Expression getSimpleUdtDefaultValue(AST ast, SimpleDataTypeDef udt) {
    if (udt.getObjectType() == GspElementObjectType.None) {
      if (udt.getMDataType() == GspElementDataType.String) {
        StringLiteral stringLiteral = ast.newStringLiteral();
        stringLiteral.setLiteralValue("");
        return stringLiteral;
      } else if (udt.getMDataType() == GspElementDataType.Boolean) {
        return ast.newBooleanLiteral(false);
      } else if (udt.getMDataType() == GspElementDataType.Integer) {
        return ast.newNumberLiteral("0");
      } else if (udt.getMDataType() == GspElementDataType.Decimal) {
        ClassInstanceCreation cls = ast.newClassInstanceCreation();
        cls.setType(new TypeInfo(BigDecimal.class) {{
          setUseFullName(true);
        }}.getType(ast));
        StringLiteral stringLiteral1 = ast.newStringLiteral();
        stringLiteral1.setLiteralValue("0.0");
        cls.arguments().add(stringLiteral1);
        return cls;
      }
    }
    return null;
  }

  private void addCreateEnrichedAssoUdtStatement(MethodInvocation methodInvocation,
      String objectCode, IGspCommonField element) {
    ClassInstanceCreation classInstanceCreation = ast.newClassInstanceCreation();
    classInstanceCreation.setType(ast.newSimpleType(ast.newName(
        mgrImplGenContext.getMgrGenContext().getFSGeneratorContext().getEntityGeneratorContext()
            .getEnrichedUdtAssoTypes().get(objectCode).get(element.getLabelID()).getFullName())));
    classInstanceCreation.arguments().add(ast.newNullLiteral());
    methodInvocation.arguments().add(classInstanceCreation);
  }
    private void addMethodCreateManagerExpression(MethodInvocation methodInvocationCreateManager) {
        MethodInvocation methodInvocation = ast.newMethodInvocation();
        methodInvocation.setName(ast.newSimpleName("getUdtFactory"));
        methodInvocation.setExpression(ast.newName(UdtManagerUtil.class.getTypeName()));

        methodInvocationCreateManager.setExpression(methodInvocation);
    }

    private void addReturnDataStatement(Block block) {
        ReturnStatement returnStatement = ast.newReturnStatement();
        returnStatement.setExpression(ast.newSimpleName("data"));
        block.statements().add(returnStatement);
    }


    @Override
    protected String getMethodName() {
        return "innerCreateData";
    }

    @Override
    protected ArrayList<ParameterInfo> getParameterCollection() {
        return null;
    }

    @Override
    protected TypeInfo getReturnType() {
        return new TypeInfo(IEntityData.class);
    }

    @Override
    protected ArrayList<Modifier.ModifierKeyword> getAccessModifiers() {
        return new ArrayList<>(Arrays.asList(new Modifier.ModifierKeyword[]{Modifier.ModifierKeyword.PROTECTED_KEYWORD}));
    }

    @Override
    protected boolean getIsOverride() {
        return true;
    }
}
